package com.wutong.eams.filter;

import com.wutong.eams.entity.User;
import com.wutong.eams.enums.Role;
import com.wutong.eams.service.UserAuthenticService;
import com.wutong.eams.utils.JwtUtil;
import com.wutong.eams.utils.RedisUtil;
import com.wutong.eams.utils.RouterUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.Map;

@Slf4j
@WebFilter("/*")
public class JwtAuthFilter extends BaseFilter {
    private static final Map<String, RouterUtil.MatchType> WHITE_LIST = Map.of(
            "/page/login", RouterUtil.MatchType.EXACT,
            "/static", RouterUtil.MatchType.PREFIX,
            "/api/auth/login", RouterUtil.MatchType.EXACT,
            "/favicon.ico", RouterUtil.MatchType.EXACT
    );

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (RouterUtil.isWhiteListed(request.getRequestURI(), WHITE_LIST)) {
            log.info("白名单路径 {}，放行", request.getRequestURI());
            chain.doFilter(req, res);
            return;
        } else {
            log.debug("未存在与白名单中：{}，进行Token验证", request.getRequestURI());
        }

        User user = (User) request.getSession().getAttribute("user");
        if (user == null) {
            log.error("Token验证失败，未登录：不存在User对象");
            response.sendRedirect("/page/login");
            return;
        }
        String token = RedisUtil.getValue("LOGIN_TOKEN-" + user.getUsername(), String.class);
        if (token == null) {
            log.error("Token验证失败，未登录：Token为空");
            response.sendRedirect("/page/login");
            return;
        }
        // 验证Token有效性和合法性
        // 若Token有效，则放行
        JwtUtil.parseToken(token, new JwtUtil.JwtExceptionHandel<Void>() {
            @Override
            public Void commonHandler(Claims claims) throws Exception {
                String role = claims.get("role", String.class);
                if (role == null || role.isEmpty()) {
                    log.debug("claims: {}", claims);
                    log.error("Token验证失败，角色为空");
                    try {
                        response.sendRedirect("/page/login");
                        return null;
                    } catch (IOException e) {
                        log.error("Token验证失败，角色为空", e);
                        return null;
                    }
                }
                UserAuthenticService service = new UserAuthenticService();
                if (service.isMatchRole(claims.get("username", String.class), Role.valueOf(role.toUpperCase()))) {
                    try {
                        log.info("Token验证成功");
                        chain.doFilter(req, res);
                        return null;
                    } catch (IOException e) {
                        log.error("Token验证失败，web服务器IO异常", e);
                        return null;
                    }
                } else {
                    log.error("Token验证失败，角色不匹配");
                    return null;
                }
            }

            @Override
            public Void expiredHandler(ExpiredJwtException e) {
                log.error("Token过期", e);
                try {
                    response.sendError(401, "Unauthorized");
                    RedisUtil.removeValue("LOGIN_TOKEN-" + user.getUsername());
                    response.sendRedirect("/page/login");
                } catch (IOException e1) {
                    log.error("Token过期，web服务器IO异常", e1);
                }
                return null;
            }

            @Override
            public Void invalidHandler(IllegalArgumentException e) {
                log.error("Token无效", e);
                try {
                    response.sendError(401, "Unauthorized");
                    RedisUtil.removeValue("LOGIN_TOKEN-" + user.getUsername());
                    response.sendRedirect("/page/login");
                } catch (IOException e1) {
                    log.error("Token无效，web服务器IO异常", e1);
                }
                return null;
            }

            @Override
            public Void otherHandler(Exception e) {
                log.error("Token验证失败", e);
                try {
                    response.sendError(500, "Internal Server Error");
                    RedisUtil.removeValue("LOGIN_TOKEN-" + user.getUsername());
                    response.sendRedirect("/page/login");
                } catch (IOException e1) {
                    log.error("Token验证失败，web服务器IO异常", e1);
                }
                return null;
            }
        });
    }
}

